package com.gameShare.utils

import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.databind.ObjectMapper
import com.gameShare.enums.misc.ResponseCodeEnum
import com.gameShare.exceptions.BusinessException
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.redis.RedisConnectionFailureException
import org.springframework.data.redis.core.StringRedisTemplate
import org.springframework.stereotype.Component
import java.time.Duration

@Component
class RedisUtils(
    private val redisStringTemplate: StringRedisTemplate,
    private val objectMapper: ObjectMapper
) {

    private val logger = LoggerFactory.getLogger(RedisUtils::class.java)

    /**
     * 从redis通过键返回值
     *
     * @param key 键
     * @return 值
     */
    fun get(key : String): String? {
        try {
            return redisStringTemplate.opsForValue().get(key)
        } catch (e : RedisConnectionFailureException){
            logger.warn("从redis获取键:\t${key}键", e)
            throw BusinessException(ResponseCodeEnum.SERVER_ERROR.code, "redis未启动")
        }
    }

    /**
     * 通过键设置redis的值
     *
     * @param key 键
     * @param value 值
     */
    fun set(key : String, value : String){
        try {
            redisStringTemplate.opsForValue().set(key, value)
        } catch (e : RedisConnectionFailureException){
            logger.warn("保存键:\t${key}值:\t${value}到redis失败", e)
            throw BusinessException(ResponseCodeEnum.SERVER_ERROR.code, "redis未启动")
        }
    }

    /**
     * 通过键设置值还有过期时间
     *
     * @param key 键
     * @param value 值
     * @param expire 过期时间
     */
    fun set(key: String, value: String, expire: Long) {
        try {
            redisStringTemplate.opsForValue().set(key, value, Duration.ofSeconds(expire))
        } catch (e : RedisConnectionFailureException){
            logger.error("保存键:\t${key}值:\t${value}过期时间:${expire}到redis失败", e)
            throw BusinessException(ResponseCodeEnum.SERVER_ERROR.code, "redis未启动")
        }
    }

    /**
     * 删除键
     *
     * @param key 键
     */
    fun del(key : String){
        try {
            redisStringTemplate.delete(key)
        } catch (e : RedisConnectionFailureException){
            logger.warn("删除键:\t${key}失败", e)
            throw BusinessException(ResponseCodeEnum.SERVER_ERROR.code, "redis未启动")
        }
    }

    /**
     * 从redis保存kotlin对象
     *
     * @param T kotlin对象类型
     * @param key  键
     * @param data 值
     */
    fun <T> saveSerializableObject(key : String, data : T){
        try {
            val objectJson = objectMapper.writeValueAsString(data)
            objectJson?.let { redisStringTemplate.opsForValue().set(key, it) }
        } catch (e : JsonProcessingException){
            logger.error("保存对象序列化失败,key为${key}对象类名为${data}", e)
        } catch (e : RedisConnectionFailureException){
            logger.error("保对象${data}失败", e)
        }
    }

    /**
     * 从redis保存kotlin对象并且设置过期时间
     *
     * @param T kotlin对象类型
     * @param key  键
     * @param data 值
     * @param expire 过期时间
     */
    fun <T> saveSerializableObject(key : String, data : T, expire : Long){
        try {
            val objectJson = objectMapper.writeValueAsString(data)
            objectJson?.let { redisStringTemplate.opsForValue().set(key, it, Duration.ofSeconds(expire)) }
        } catch (e : JsonProcessingException){
            logger.error("保存对象序列化失败,key为${key}过期时间为${expire}对象类名为${data}", e)
        } catch (e : RedisConnectionFailureException){
            logger.error("保存对象${data}并赋予过期时间${expire}失败", e)
        }
    }

    /**
     * 从redis中获取kotlin对象
     *
     * @param T kotlin对象类型
     * @param key 键
     * @param clazz kotlin对象的class类型
     * @return T型kotlin对象
     */
    fun  <T> getSerializableObject(key : String, clazz: Class<T>): T? {
        val orgJson : String = get(key)
            ?: throw BusinessException(ResponseCodeEnum.ARGUMENT_ERROR)
        return objectMapper.readValue(orgJson, clazz)
            ?: throw BusinessException(ResponseCodeEnum.ARGUMENT_ERROR)
    }

}